using System;
using dnAnalytics.LinearAlgebra;
using NUnit.Framework;

namespace dnAnalytics.Tests.LinearAlgebra
{
    [TestFixture]
    public class DenseMatrixTests : MatrixTests
    {
        public override Matrix GetMatrix(int order)
        {
            return new DenseMatrix(order);
        }

        public override Matrix GetMatrix(int m, int n)
        {
            return new DenseMatrix(m, n);
        }

        public override Matrix GetMatrix(double[,] data)
        {
            return new DenseMatrix(data);
        }

        public override Vector GetVector(int size)
        {
            return new DenseVector(size);
        }

        public override Vector GetVector(double[] data)
        {
            return new DenseVector(data);
        }

        [Test]
        public void ConstructorValue()
        {
            const int rows = 4;
            const int cols = 3;
            DenseMatrix matrix =new DenseMatrix(rows,cols,2.0);
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Assert.AreEqual(2.0, matrix[row,col]);
                }
            }
        }

        [Test]
        public void ConstructorSparse()
        {
            const int rows = 4;
            const int cols = 3;
            SparseMatrix sparse = new SparseMatrix(rows, cols);
            sparse[0, 1] = 2.0;
            sparse[3, 2] = 3.0;
            DenseMatrix matrix = new DenseMatrix(sparse);
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Assert.AreEqual(sparse[row, col], matrix[row, col]);
                }
            }

        }

        [Test]
        public void DenseOperatorDivide()
        {
            const int rows = 4;
            const int cols = 3; 
            DenseMatrix matrix = new DenseMatrix(rows, cols, 4.0);
            matrix = matrix/2.0;

            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Assert.AreEqual(2.0, matrix[row, col]);
                }
            }


        }

        [Test]
        public void OperatorAdd()
        {
            const int rows = 4;
            const int cols = 3;
            DenseMatrix matrix1 = new DenseMatrix(rows, cols);
            DenseMatrix matrix2 = new DenseMatrix(rows, cols);

            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    matrix1[row, col] = matrix2[row, col] = 1.0;
                }
            }

            DenseMatrix result = matrix1 + matrix2;
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                   Assert.AreEqual(2.0, result[row,col]);
                }
            }
        }

        [Test]
        public void OperatorSubtract()
        {
            const int rows = 4;
            const int cols = 3;
            DenseMatrix matrix1 = new DenseMatrix(rows, cols);
            DenseMatrix matrix2 = new DenseMatrix(rows, cols);

            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    matrix1[row, col] = matrix2[row, col] = 1.0;
                }
            }

            DenseMatrix result = matrix1 - matrix2;
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Assert.AreEqual(0.0, result[row, col]);
                }
            }
        }

        [Test]
        public void DenseOperatorNegate()
        {
            const int rows = 4;
            const int cols = 3;
            DenseMatrix matrix = new DenseMatrix(rows, cols, 2.0);
            matrix = -matrix;
            for (int row = 0; row < rows; row++)
            {
                for (int col = 0; col < cols; col++)
                {
                    Assert.AreEqual(-2.0, matrix[row, col]);
                }
            }
        }
        
        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatorMultiplyMatrixScalarNull()
        {
            DenseMatrix DenseMatrix = null;
            DenseMatrix result = DenseMatrix * .1234;
        }

       
        [Test]
        public void OperatorMultiplyScalar()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseMatrix result = .1234 * DenseMatrix;
            for (int i = 0; i < DenseMatrix.Rows; i++)
            {
                for (int j = 0; j < DenseMatrix.Columns; j++)
                {
                    Assert.AreEqual(square[i, j] * .1234, result[i, j], Constants.AcceptableError);
                }
            }

            DenseMatrix = new DenseMatrix(wide);
            result = .1234 * DenseMatrix;
            for (int i = 0; i < DenseMatrix.Rows; i++)
            {
                for (int j = 0; j < DenseMatrix.Columns; j++)
                {
                    Assert.AreEqual(wide[i, j] * .1234, result[i, j], Constants.AcceptableError);
                }
            }

            DenseMatrix = new DenseMatrix(tall);
            result = .1234 * DenseMatrix;
            for (int i = 0; i < DenseMatrix.Rows; i++)
            {
                for (int j = 0; j < DenseMatrix.Columns; j++)
                {
                    Assert.AreEqual(tall[i, j] * .1234, result[i, j], Constants.AcceptableError);
                }
            }
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatorMultiplyScalarMatrixNull()
        {
            DenseMatrix DenseMatrix = null;
            DenseMatrix result = .1234 * DenseMatrix;
        }

        [Test]
        public void OperatorMultiplyMatrix()
        {
            DenseMatrix matrix1 = new DenseMatrix(square);
            DenseMatrix matrix2 = new DenseMatrix(square);
            DenseMatrix result = matrix1 * matrix2;

            Assert.AreEqual(3, result.Rows);
            Assert.AreEqual(3, result.Columns);
            Assert.AreEqual(15.73, result[0, 0], Constants.AcceptableError);
            Assert.AreEqual(-18.15, result[0, 1], Constants.AcceptableError);
            Assert.AreEqual(-22.99, result[0, 2], Constants.AcceptableError);
            Assert.AreEqual(-9.68, result[1, 0], Constants.AcceptableError);
            Assert.AreEqual(13.31, result[1, 1], Constants.AcceptableError);
            Assert.AreEqual(16.94, result[1, 2], Constants.AcceptableError);
            Assert.AreEqual(-24.2, result[2, 0], Constants.AcceptableError);
            Assert.AreEqual(52.03, result[2, 1], Constants.AcceptableError);
            Assert.AreEqual(70.17999999999999, result[2, 2], Constants.AcceptableError);

            matrix1 = new DenseMatrix(wide);
            matrix2 = new DenseMatrix(tall);
            result = matrix1 * matrix2;

            Assert.AreEqual(2, result.Rows);
            Assert.AreEqual(2, result.Columns);
            Assert.AreEqual(15.73, result[0, 0], Constants.AcceptableError);
            Assert.AreEqual(-18.15, result[0, 1], Constants.AcceptableError);
            Assert.AreEqual(-9.68, result[1, 0], Constants.AcceptableError);
            Assert.AreEqual(13.31, result[1, 1], Constants.AcceptableError);

            matrix1 = new DenseMatrix(tall);
            matrix2 = new DenseMatrix(wide);
            result = matrix1 * matrix2;

            Assert.AreEqual(3, result.Rows);
            Assert.AreEqual(3, result.Columns);
            Assert.AreEqual(1.21, result[0, 0], Constants.AcceptableError);
            Assert.AreEqual(0, result[0, 1], Constants.AcceptableError);
            Assert.AreEqual(-1.21, result[0, 2], Constants.AcceptableError);
            Assert.AreEqual(0, result[1, 0], Constants.AcceptableError);
            Assert.AreEqual(1.21, result[1, 1], Constants.AcceptableError);
            Assert.AreEqual(2.42, result[1, 2], Constants.AcceptableError);
            Assert.AreEqual(4.84, result[2, 0], Constants.AcceptableError);
            Assert.AreEqual(15.73, result[2, 1], Constants.AcceptableError);
            Assert.AreEqual(26.62, result[2, 2], Constants.AcceptableError);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatotMulitplyMatrixNull()
        {
            DenseMatrix matrix1 = null;
            DenseMatrix matrix2 = new DenseMatrix(square);
            DenseMatrix result = matrix1 * matrix2;
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatotMulitplyMatrixMatrixNull()
        {
            DenseMatrix matrix1 = new DenseMatrix(square);
            DenseMatrix matrix2 = null;
            DenseMatrix result = matrix1 * matrix2;
        }

        [Test]
        [ExpectedException(typeof(NotConformableException))]
        public void DenseOperatotMulitplyMatrixMatrixNotConformable()
        {
            DenseMatrix matrix1 = new DenseMatrix(tall);
            DenseMatrix matrix2 = new DenseMatrix(tall);
            DenseMatrix result = matrix1 * matrix2;
        }

        [Test]
        public void DenseOperatorMultiplyMatrixVector()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            DenseVector result = DenseMatrix * DenseVector;
            Assert.AreEqual(DenseMatrix.Rows, result.Count);
            Assert.AreEqual(-16.94, result[0], Constants.AcceptableError);
            Assert.AreEqual(9.68, result[1], Constants.AcceptableError);
            Assert.AreEqual(29.04, result[2], Constants.AcceptableError);

            DenseMatrix = new DenseMatrix(wide);
            DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            result = DenseMatrix * DenseVector;
            Assert.AreEqual(DenseMatrix.Rows, result.Count);
            Assert.AreEqual(-16.94, result[0], Constants.AcceptableError);
            Assert.AreEqual(9.68, result[1], Constants.AcceptableError);

            DenseMatrix = new DenseMatrix(tall);
            DenseVector = new DenseVector(new double[] { 1.1, 2.2 });
            result = DenseMatrix * DenseVector;
            Assert.AreEqual(DenseMatrix.Rows, result.Count);
            Assert.AreEqual(-6.05, result[0], Constants.AcceptableError);
            Assert.AreEqual(2.42, result[1], Constants.AcceptableError);
            Assert.AreEqual(7.26, result[2], Constants.AcceptableError);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatorMultiplyMatrixNullVector()
        {
            DenseMatrix DenseMatrix = null;
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            DenseVector result = DenseMatrix * DenseVector;
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatorMultiplyMatrixVectorNull()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = null;
            DenseVector result = DenseMatrix * DenseVector;
        }

        [Test]
        [ExpectedException(typeof(NotConformableException))]
        public void DenseOperatorMultiplyMatrixVectorNotConformable()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2 });
            DenseVector result = DenseMatrix * DenseVector;
        }

        [Test]
        public void OperatorMultiplyVector()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            DenseVector result = DenseVector * DenseMatrix;
            Assert.AreEqual(DenseMatrix.Columns, result.Count);
            Assert.AreEqual(-15.73, result[0], Constants.AcceptableError);
            Assert.AreEqual(18.15, result[1], Constants.AcceptableError);
            Assert.AreEqual(22.99, result[2], Constants.AcceptableError);

            DenseMatrix = new DenseMatrix(wide);
            DenseVector = new DenseVector(new double[] { 1.1, 2.2 });
            result = DenseVector * DenseMatrix;
            Assert.AreEqual(DenseMatrix.Columns, result.Count);
            Assert.AreEqual(-1.21, result[0], Constants.AcceptableError);
            Assert.AreEqual(0, result[1], Constants.AcceptableError);
            Assert.AreEqual(1.21, result[2], Constants.AcceptableError);

            DenseMatrix = new DenseMatrix(tall);
            DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            result = DenseVector * DenseMatrix;
            Assert.AreEqual(DenseMatrix.Columns, result.Count);
            Assert.AreEqual(-15.73, result[0], Constants.AcceptableError);
            Assert.AreEqual(18.15, result[1], Constants.AcceptableError);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void DenseOperatorMultiplyVectorMatrixNull()
        {
            DenseMatrix DenseMatrix = null;
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2, 3.3 });
            DenseVector result = DenseVector * DenseMatrix;
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void OperatorMultiplyVectorNull()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = null;
            DenseVector result = DenseVector * DenseMatrix;
        }

        [Test]
        [ExpectedException(typeof(NotConformableException))]
        public void DenseOperatorMultiplyVectorMatrixNotConformable()
        {
            DenseMatrix DenseMatrix = new DenseMatrix(square);
            DenseVector DenseVector = new DenseVector(new double[] { 1.1, 2.2 });
            DenseVector result = DenseVector * DenseMatrix;
        }
    }
}